package student.aistar.mypools;

import student.aistar.util.JdbcUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;

public class MyDataSource {
    public int maxConn = 8;
    public int initialSize = 3;
    public int currentSize = 0;

    public LinkedList<Connection> pool = new LinkedList<>();
    private static volatile  MyDataSource instance = null;

    private MyDataSource() {
        //初始化一些拥有的连接放入到"连接池中去"
        for (int i = 0; i < initialSize; i++) {
            pool.add(createConnection());
        }
    }

    public static MyDataSource getInstance(){
        if(null==instance){
            synchronized (MyDataSource.class){
                if(null==instance){
                    instance = new MyDataSource();
                }
            }
        }
        return instance;
    }

    //提供一个方法来获取链接
    //目的 -从连接池中得到一根连接
    public Connection getConnection(){
        //1.如果连接池中有可能的连接,那么直接从pool中返回一个
        //2.保证多线程情况下,不能有两个线程争抢到同一个connection
        synchronized (pool){
            if(pool.size() > 0){
                System.out.println("===直接从连接池中获取===");
                currentSize++;
                //说明连接池中有可用的连接
                //集合中删除一个,删除的这个连接直接返回出去
                return pool.removeLast();
            }else if(currentSize <= maxConn){
                System.out.println("===可用的连接数已用完");
                currentSize++;
                //隐含的条件 - 拿到之后, 需要把这跟连接放到连接池中 - 有可能
                return createConnection();
            }
        }
        return null;
    }

    private Connection createConnection() {
        //jdbc - 代理对象
        Connection connProxy = null;
        try {
            Connection connection = JdbcUtil.getConnection();
            connProxy = (Connection) Proxy.newProxyInstance(connection.getClass().getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result = null;
                    //只要使用代理对象调用目标对象的接口里的任何方法, 都会促发重写的invoke方法
                    //close, commit, rollback
                    if(method.getName().equals("close") && currentSize <= maxConn){
                        System.out.println("放回到连接池中");
                        pool.add(connection);
                        currentSize--;
                    }else if(method.getName().equals("close") && currentSize > maxConn){
                        System.out.println("真正释放出来");
                        currentSize--;
                        if(connection != null)
                            connection.close();
                    }else
                        result = method.invoke(connection, args);
                    return result;
                }
            });
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return connProxy;
    }

    public static void main(String[] args) throws SQLException {
        Connection conn1 = MyDataSource.getInstance().getConnection();

        Connection conn2 = MyDataSource.getInstance().getConnection();

        Connection conn3 = MyDataSource.getInstance().getConnection();

        //代理对象调用 - 放回到连接池中
        Connection conn4 = MyDataSource.getInstance().getConnection();
        Connection conn5 = MyDataSource.getInstance().getConnection();
        Connection conn6 = MyDataSource.getInstance().getConnection();
        Connection conn7 = MyDataSource.getInstance().getConnection();
        Connection conn8 = MyDataSource.getInstance().getConnection();
        Connection conn9 = MyDataSource.getInstance().getConnection();
    }
}
